#include <stdlib.h>
#include <stdio.h>
#include <sys/socket.h>
#include <sys/types.h>
#include <sys/ipc.h>
#include <unistd.h>
#include <netinet/in.h>
#include <netdb.h>
#include <arpa/inet.h>
#include <errno.h>
#include <string.h>
#include <sys/shm.h>
#include <semaphore.h>
#include "filme.h"
#include "req.h"

/*
 * Função que recebe um filme por parametro, trata o mesmo e envia para
 * a máquina intermediária para passar ao servidor
 */
int sendFilme(char opcao[], tFilme filme) {
    int sock, ends = 1;
    sockaddr_in inter;
    hostent *he;
    char package[400];


    strcpy(package, opcao);
    strcat(package, filmeToChar(filme));

    if (!preparar(&sock, &inter, he, PORTA)) return 0;
    inter.sin_port = htons(PORTA);
    if (!conectar(sock, &inter)) return 0;
    if (!enviar(sock, package)) return 0;

    if (!strcmp(opcao, "add")) {
        do_add(sock);
        ends = 5;
    }

    close(sock);

    int sock_recv;
    int addr_in_size;
    sockaddr_in inter_addr, client_addr;

    if (!preparar(&sock, &inter_addr, he, 12891)) return 0;
    if (bind(sock, (sockaddr *) & inter_addr, sizeof (sockaddr)) == -1) {
        perror("bind");
        exit(1);
    }
    if (listen(sock, 50) == -1) {
        perror("listen");
        exit(1);
    }
    while (ends < 4) {
        addr_in_size = sizeof (sockaddr_in);
        while ((sock_recv = accept(sock, (sockaddr *) & client_addr, &addr_in_size)) == -1) {
            perror("accept");
        }
        ends++;
        if (!fork()) {
            close(sock);
            printf("\nConnection:\nReceived connection from: %s\n", inet_ntoa(client_addr.sin_addr));

            char filmeChar[400];
            tFilme *filme;
            receber(sock_recv, filmeChar, 400);
            if (strcmp(get_cmd(filmeChar), "end")) {
                filme = charToFilme(filmeChar);
                printFilme(*filme);
                receber(sock_recv, filmeChar, 400);
            }
            close(sock_recv);
            exit(0);
        }

        close(sock_recv);
    }
    close(sock);
    while (wait(NULL) > 0);
    return 1;
}

/*
 * Função que passa para a requisição para ser adicionado um filme
 */
void do_add(int sock) {
    char *msg = (char *) malloc(100);
    receber(sock, msg, 100);
}

/*
 * Função que pera o ip da maquina do cliente
 */
char* get_ip_client(char *pack) {
    char *ip = (char *) malloc(20);
    int i, j = 0;
    for (i = strlen(pack) - 1; i >= 0; i--) {
        if (pack[i] == '|') break;
    }
    for (i = i + 1; i < strlen(pack); i++) {
        ip[j] = pack[i];
        ip[j + 1] = '\0';
        j++;
    }
    return ip;
}

/*
 * Funcao que pega a opção escolhida pelo cliente na execução
 */
char *get_cmd(char *pack) {
    char *cmd = (char *) malloc(4);
    strncpy(cmd, pack, 3);
    cmd[3] = '\0';
    return cmd;
}

/*
 * Funcao que remove o ip da sequencia de caracteres
 */
void take_out_ip(char *pack) {
    int i;
    for (i = strlen(pack) - 1; i >= 0; i--) {
        if (pack[i] == '|') {
            break;
        }
    }
    pack[i] = '\0';
    return;
}

/*
 * Função que recebe um filme por parametro,
 * e o converte para uma sequencia de caracteres separados por pipe
 */
char *filmeToChar(tFilme filme) {
    char *buffer = (char *) malloc(400);
    sprintf(buffer, "|%s|%s|%s|%d|", filme.titulo, filme.ator, filme.genero, filme.ano);
    return buffer;
}

/*
 * Função que recebe uma sequencia de caracteres separados por pipe por parametro,
 * e o converte para um filme 
 */
tFilme *charToFilme(char *buffer) {
    int i = 0, controle = 0, j = 0;
    char ano[5];
    tFilme *filme = (tFilme *) malloc(sizeof (tFilme));
    for (i = 0; i < strlen(buffer); i++) {
        if (buffer[i] == '|') {
            j = 0;
            controle++;
        } else {
            switch (controle) {
                case 0:
                    continue;
                case 1:
                    filme->titulo[j] = buffer[i];
                    filme->titulo[j + 1] = '\0';
                    j++;
                    break;
                case 2:
                    filme->ator[j] = buffer[i];
                    filme->ator[j + 1] = '\0';
                    j++;
                    break;
                case 3:
                    filme->genero[j] = buffer[i];
                    filme->genero[j + 1] = '\0';
                    j++;
                    break;
                case 4:
                    ano[j] = buffer[i];
                    ano[j + 1] = '\0';
                    j++;
                    break;
                default:
                    i = strlen(buffer) + 1;
            }
        }
    }

    filme->ano = atoi(ano);
    return filme;
}

/*
 * Função que recebe um filme por parametro,
 * e imprime o mesmo
 */
void printFilme(tFilme filme) {
    printf("\nFilme:\nTitulo: %s\n", filme.titulo);
    printf("Ator: %s\n", filme.ator);
    printf("Genero: %s\n", filme.genero);
    printf("Ano: %d\n\n", filme.ano);
}

